import os.path
import time
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from config import project_dir
from database import get_db
from model.cname import CName
from schema.cdn_config_schema import CdnConfigCreate, CdnConfigUpdate
from model.cdn_config import CdnConfig
from model.cdn_node import CdnNode
import subprocess
import paramiko
import socket
from datetime import datetime
from schema.user_schema import UserCreate, User as UserSchema
from route.user_api import get_current_user
from services.cdn_config import CdnConfigService

import logging

from utils.dns_util import DNSUtil

logger = logging.getLogger("fast-run")

# 服务器列表
servers_list = [
    {"host": "103.118.40.245", "user": "root"}
    # 可以继续添加
]

router = APIRouter(tags=["api"],
                   dependencies=[Depends(get_current_user)]  # 🔑 所有路由都需要认证
                   )

ingress_yaml_path = f"{project_dir}/cdn_yaml/ingress.yaml"
nginx_yaml_path = f"{project_dir}/cdn_yaml/nginx.yaml"
ssl_yaml_path = f"{project_dir}/cdn_yaml/ssl.yaml"


@router.post("/add")
def register(cdn_config: CdnConfigCreate, db: Session = Depends(get_db)):
    try:
        # 检查是否已存在
        existing = db.query(CdnConfig).filter(CdnConfig.domain == cdn_config.domain).first()
        if existing:
            return {"message": "CdnConfig already exists"}

        cname = db.query(CName).filter(CName.id == cdn_config.cnameId).first()

        # 创建新配置
        db_cdn_config = CdnConfig(
            user_id=cdn_config.userId,
            domain=cdn_config.domain,
            originIp=cdn_config.originIp,
            cname=cname,
            ifDeploy=False,
            fullchain=None,
            privkey=None
        )

        # 处理节点关联
        if cdn_config.nodeIds:
            nodes = db.query(CdnNode).filter(CdnNode.id.in_(cdn_config.nodeIds)).all()
            db_cdn_config.nodes = nodes

        db.add(db_cdn_config)
        db.commit()
        db.refresh(db_cdn_config)

        return {"message": "success", "cdnConfigId": db_cdn_config.id}
    except Exception as e:
        db.rollback()
        logger.error(e)
        raise HTTPException(status_code=500, detail=str(e))


@router.put("/update/{cdn_config_id}")
def update_cdn_config(cdn_config_id: int, cdn_config: CdnConfigUpdate, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")

        # 更新普通字段
        for key, value in cdn_config.dict(exclude={"nodeIds"}).items():
            if value is not None:
                setattr(db_cdn_config, key, value)

        # 更新节点关联
        if cdn_config.nodeIds is not None:
            nodes = db.query(CdnNode).filter(CdnNode.id.in_(cdn_config.nodeIds)).all()
            db_cdn_config.nodes = nodes

        db.commit()
        db.refresh(db_cdn_config)
        return db_cdn_config
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/delete/{cdn_config_id}")
def delete_cdn_config(cdn_config_id: int, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")
        db.delete(db_cdn_config)
        db.commit()
        return db_cdn_config
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/query/{domain}")
def query_cdn_config(domain: str, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.domain == domain).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")
        return db_cdn_config
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/query_all")
def query_all_cdn_configs(db: Session = Depends(get_db)):
    try:
        db_cdn_configs = db.query(CdnConfig).all()
        results = []

        for config in db_cdn_configs:
            record = {
                "id": config.id,
                "domain": config.domain,
                "originIp": config.originIp,
                "ifDeploy": config.ifDeploy,
                "fullchain": config.fullchain,
                "privkey": config.privkey,
                "cert_apply_time": config.cert_apply_time,
                # 节点信息
                "nodes": [
                    {
                        "id": node.id,
                        "name": node.name,
                        "host": node.host,
                        "ssh_user": node.ssh_user,
                        "ssh_port": node.ssh_port,
                        "status": node.status
                    }
                    for node in config.nodes
                ],
                "cname": {
                    "id": config.cname.id,
                    "domain": config.cname.domain,
                    "ttl": config.cname.ttl,
                    "status": config.cname.status,
                    "created_at": config.cname.created_at,
                    "updated_at": config.cname.updated_at
                } if config.cname else None
            }
            results.append(record)

        return results
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


def _update_core_dns(zone_file, domain):
    os.makedirs("/etc/coredns/zones", exist_ok=True)

    corefile_path = "/etc/coredns/Corefile"
    zonefile_path = f"/etc/coredns/zones/{domain}.zone"

    # 写入 zone 文件
    with open(zonefile_path, "w") as f:
        f.write(zone_file)

    print("Corefile 和 zone 文件已写入完成！")
    # 重启 CoreDNS 服务
    try:
        subprocess.run(["systemctl", "restart", "coredns"], check=True)
        print("CoreDNS 服务已重启！")
    except subprocess.CalledProcessError as e:
        print("重启 CoreDNS 服务失败:", e)



def check_cert(host: str, user: str, domain: str):
    """
    核查远程服务器是否申请了证书
    返回：
      - 如果存在: (True, fullchain_content, privkey_content)
      - 如果不存在: (False, None, None)
    """
    base_path = f"/etc/letsencrypt/live/{domain}"
    fullchain_path = f"{base_path}/fullchain.pem"
    privkey_path = f"{base_path}/privkey.pem"

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        # SSH免密登录
        client.connect(hostname=host, username=user)

        # 检查文件是否存在
        check_cmd = f'test -f {fullchain_path} && test -f {privkey_path} && echo "YES" || echo "NO"'
        stdin, stdout, stderr = client.exec_command(check_cmd)
        exists = stdout.read().decode().strip() == "YES"

        if not exists:
            return False, None, None

        # 读取证书内容
        stdin, stdout, stderr = client.exec_command(f'cat {fullchain_path}')
        fullchain_content = stdout.read().decode()

        stdin, stdout, stderr = client.exec_command(f'cat {privkey_path}')
        privkey_content = stdout.read().decode()

        return True, fullchain_content, privkey_content

    finally:
        client.close()


def get_all_ips(cdn_config_id: int, db: Session):
    """
    获取指定 CDN 配置关联的所有节点 IP
    """
    cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
    if not cdn_config:
        return []
    return [node.host for node in cdn_config.nodes if node.status]  # 只返回可用节点



def apply_cert_on_remote(server: dict, domain: str, email: str):
    """
    在远程节点申请证书，并返回 fullchain 和 privkey 内容
    server: {"host": str, "user": str, "ssh_port": int, "ssh_key": Optional[str]}
    """
    remote_cert_dir = f"/etc/letsencrypt/live/{domain}/"
    fullchain_path = f"{remote_cert_dir}/fullchain.pem"
    privkey_path = f"{remote_cert_dir}/privkey.pem"

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    # 使用密钥或默认认证

    try:
        client.connect(hostname=server['host'], username=server['user'])
        # 执行 certbot 申请证书
        certbot_cmd = (
            f"sudo certbot certonly --standalone "
            f"-d {domain} --non-interactive --agree-tos --email {email}"
        )
        print(f"开始证书申请=====\n{certbot_cmd}")
        stdin, stdout, stderr = client.exec_command(certbot_cmd)
        exit_status = stdout.channel.recv_exit_status()
        if exit_status != 0:
            err_msg = stderr.read().decode()
            raise HTTPException(status_code=500, detail=f"远程节点证书申请失败: {err_msg}")

        # 使用 SFTP 读取证书内容
        sftp = client.open_sftp()
        try:
            with sftp.file(fullchain_path, "r") as f:
                fullchain = f.read().decode()
            with sftp.file(privkey_path, "r") as f:
                privkey = f.read().decode()
        finally:
            sftp.close()
    except Exception as e:
        print(e)
    finally:
        client.close()

    return fullchain, privkey


def update_core_file(domain: str):
    """
    更新corefile
    :return:
    """
    corefile_path = "/etc/coredns/Corefile"

    # 读取已有配置
    if os.path.exists(corefile_path):
        with open(corefile_path, "r") as f:
            existing_corefile = f.read()
    else:
        existing_corefile = """
. {
    forward . 8.8.8.8 1.1.1.1
    log
    errors
    cache 30
}

        """

    # 只追加新的域名配置，避免重复
    if domain not in existing_corefile:
        domain_corefile = f"""
{domain} {{
    file /etc/coredns/zones/{domain}.zone
    log
    errors
    cache 30
}}
    """
        existing_corefile += domain_corefile

    # 写回 Corefile
    with open(corefile_path, "w") as f:
        f.write(existing_corefile)

    print("Corefile 更新完成")


@router.put("/deploy/{cdn_config_id}")
def deploy_cdn_config(cdn_config_id: int, db: Session = Depends(get_db)):
    return CdnConfigService.deploy_cdn_config(cdn_config_id,db)


def remove_core_dns(domain: str):
    """
    删除 CoreDNS 的 zone 文件和 Corefile 配置
    """
    corefile_path = "/etc/coredns/Corefile"
    zonefile_path = f"/etc/coredns/zones/{domain}.zone"

    # 删除 zone 文件
    if os.path.exists(zonefile_path):
        os.remove(zonefile_path)

    # 删除 Corefile 中的域名配置
    if os.path.exists(corefile_path):
        with open(corefile_path, "r") as f:
            lines = f.readlines()

        new_lines = []
        skip = False
        for line in lines:
            if domain in line:  # 域名配置开始
                skip = True
            if skip and line.strip() == "}":
                skip = False
                continue
            if not skip:
                new_lines.append(line)

        with open(corefile_path, "w") as f:
            f.writelines(new_lines)

    # 重启 CoreDNS
    try:
        subprocess.run(["systemctl", "restart", "coredns"], check=True)
    except subprocess.CalledProcessError as e:
        print(f"重启 CoreDNS 失败: {e}")


def remove_nginx_conf_from_server(host, user, domain):
    """
    删除远程节点的 nginx 配置并 reload
    """
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=host, username=user)

    remote_conf_path = f"/etc/nginx/conf.d/{domain}.conf"
    ssh.exec_command(f"rm -f {remote_conf_path}")
    ssh.exec_command("nginx -s reload")

    ssh.close()
@router.put("/offline/{cdn_config_id}")
def cdn_offline(cdn_config_id: int, db: Session = Depends(get_db)):
    # 1. 查找记录
    cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
    if not cdn_config:
        raise HTTPException(status_code=404, detail="CDN 配置不存在")

    # 2. 判断当前状态
    if not cdn_config.ifDeploy:
        raise HTTPException(status_code=400, detail="该配置已处于下线状态，无需重复操作")
    return CdnConfigService.remove_cdn_config(cdn_config_id, db)
    # domain = cdn_config.domain
    #
    # # 删除 CoreDNS zone 和 Corefile 配置
    # try:
    #     remove_core_dns(domain)
    # except Exception as e:
    #     print(f"CoreDNS 清理失败: {e}")
    #
    # # 删除 nginx 配置
    # for node in cdn_config.nodes:
    #     if node.status:
    #         try:
    #             remove_nginx_conf_from_server(node.host, node.ssh_user, domain)
    #         except Exception as e:
    #             print(f"节点 {node.host} 删除 nginx 配置失败: {e}")
    #
    # # 3. 修改状态
    # cdn_config.ifDeploy = False
    # db.add(cdn_config)
    # db.commit()
    # db.refresh(cdn_config)
    #
    # # 4. 返回结果
    # return {
    #     "status": "success",
    #     "message": f"CDN 配置 {cdn_config.domain} 已下线",
    #     "id": cdn_config.id,
    #     "ifDeploy": cdn_config.ifDeploy
    # }
    return {}


@router.put("/clear_cache/{cdn_config_id}")
def clear_cache(cdn_config_id: int, db: Session = Depends(get_db)):
    """
    清空缓存
    :param cdn_config_id:
    :param db:
    :return:
    """
    # 1. 查找记录
    cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
    if not cdn_config:
        raise HTTPException(status_code=404, detail="CDN 配置不存在")

    # 2. 判断当前状态
    if not cdn_config.ifDeploy:
        raise HTTPException(status_code=400, detail="该配置已处于下线状态，无需重复操作")
    return CdnConfigService.clear_cache(cdn_config_id, db)
